import { Container } from "pixi.js";
import { Sprite_Canvas } from "@/sprites/Sprite_Canvas.js";
import { Sprite_WindowFrame } from "@/sprites/Sprite_WindowFrame.js";
import { Sprite_WindowBackground } from "@/sprites/Sprite_WindowBackground.js";
import { TextManager } from "@/managers/TextManager.js";
import { ImageManager } from "@/managers/ImageManager.js";
import { isBigCharacter } from "@/utils/ImageFileType.js";
import { ColorExtraction } from "@/utils/ColorExtraction.js";
import { TextInterpreter } from "@/utils/TextInterpreter.js";
import { config } from "@/managers/ConfigManager.js";

export class Window_Base extends Container {
    static _faceWidth = 144;
    static _faceHeight = 144;
    constructor(width = 0, height = 0, autoinit = true) {
        super();
        this._padding = 18;
        this._margin = 4;
        this._openness = 0;
        this._isOpening = false;
        this._isClosing = false;

        if (autoinit) {
            this.init(width, height);
        }
    }

    init(width, height) {
        this._absWidth = width;
        this._absHeight = height;

        width = Math.max(width, this._padding * 2);
        height = Math.max(height, this._padding * 2);
        this.windowBackground = new Sprite_WindowBackground(width, height);
        this.windowFrame = new Sprite_WindowFrame(width, height);
        this.addChild(this.windowBackground, this.windowFrame);
        this.createContents(width, height);
        this.origin.set(this._absWidth / 2, this._absHeight / 2);
        this.scale.y = 0;
        this.open();
    }

    get padding() { return this._padding; }
    set padding(value) { this._padding = value; }
    get width() { return this._absWidth; }
    get height() { return this._absHeight; }

    lineHeight() { return 36; }

    standardFontSize() { return 28; }
    standardPadding() { return 18; }
    textPadding() { return 6; }
    standardBackOpacity() { return 192; }
    contentsWidth() { return this._absWidth - this._padding * 2; }
    contentsHeight() { return this._absHeight - this._padding * 2; }
    resetTextColor() { this.changeTextColor(ColorExtraction.normalColor()); }
    resetFontSettings() {
        this.contents.fontFace = this.standardFontFace();
        this.contents.fontSize = this.standardFontSize();
        this.resetTextColor();
    }
    resetTextColor() {
        this.changeTextColor(ColorExtraction.normalColor());
    }
    standardFontFace() {
        return 'GameFont';
    }

    createContents(width, height) {
        if (!this.contents) {
            this.contents = new Sprite_Canvas(
                width - this._padding * 2,
                height - this._padding * 2
            );
            this.addChild(this.contents);
            this.contents.x = this._padding;
            this.contents.y = this._padding;
            this.contents.visible = false;
        }
    }

    resize(width, height) {
        width = Math.max(width, this._padding * 2);
        height = Math.max(height, this._padding * 2);
        this.contents.resize(width - 2 * this._padding, height - 2 * this._padding);
        this.windowFrame.resize(width, height);
        this.windowBackground.resize(width, height);
        this._absWidth = width;
        this._absHeight = height;
    }

    containPoint(x, y) {
        let { lx, ly } = this.toLocal({ x, y });
        return lx > 0 && ly > 0 && lx < this.width && ly < this.height;
    }

    update() {
        if (this.visible) {
            if (this.isOpened()) {
                for (let child of this.children) {
                    child.update?.();
                }
                this.updateSelf();
            } else if (this.isClosing()) {
                this._openness -= 32;
                if (this._openness <= 0) {
                    this._openness = 0;
                    this.contents.visible = false;
                    this.visible = false;
                    this._isClosing = false;
                }
                this.scale.y = this._openness / 255;
            } else if (this.isOpening()) {
                this._openness += 32;
                if (this._openness >= 255) {
                    this._openness = 255;
                    this.contents.visible = true;
                    this._isOpening = false;
                }
                this.scale.y = this._openness / 255;
            }
        }

    }
    /**
     * @protected
     */
    updateSelf() { }

    isClosing() { return this._isClosing; }
    close() {
        if(this.isClosed())return; 
        this._isClosing = true; 
        this._isOpening = false; 
        this._openness -= 32;
        this.visible = true;
        this.deactivate();
    }
    isOpening() { return this._isOpening; }
    open() { 
        if(this.isOpened())return;
        this._isOpening = true; 
        this._isClosing = false; 
        this.visible = true; 
    }

    isClosed() { return this._openness <= 0; }
    isOpened() { return this._openness >= 255; }
    show() { this.visible = true; }
    hide() { this.visible = false; }
    activate() { this.active = true; }
    deactivate() { this.active = false; }
    translucentOpacity() { return 160; }
    changeTextColor(color) { this.contents.textColor = color; }
    changePaintOpacity(enabled) { this.contents.paintOpacity = enabled ? 255 : this.translucentOpacity(); }
    drawText(text, x, y, maxWidth, lineHeight, align) { this.contents.drawText(text, x, y, maxWidth, lineHeight??this.lineHeight(), align); }
    textWidth(text) { return this.contents.measureTextWidth(text); }

    actorName(n) {
        let actor = n >= 1 ? $gameActors.actor(n) : null;
        return actor ? actor.name() : '';
    }

    partyMemberName(n) {
        let actor = n >= 1 ? $gameParty.members()[n - 1] : null;
        return actor ? actor.name() : '';
    }



    makeFontBigger() {
        if (this.contents.fontSize <= 96) {
            this.contents.fontSize += 12;
        }
    }

    makeFontSmaller() {
        if (this.contents.fontSize >= 24) {
            this.contents.fontSize -= 12;
        }
    }

    drawIcon(iconIndex, x, y) {
        this.contents.drawIcon(iconIndex, config.iconSize, x, y);
    }

    drawFace(faceName, faceIndex, x, y, width, height) {
        width = width || Window_Base._faceWidth;
        height = height || Window_Base._faceHeight;
        let pw = Window_Base._faceWidth;
        let ph = Window_Base._faceHeight;
        let sw = Math.min(width, pw);
        let sh = Math.min(height, ph);
        let dx = Math.floor(x + Math.max(width - pw, 0) / 2);
        let dy = Math.floor(y + Math.max(height - ph, 0) / 2);
        let sx = faceIndex % 4 * pw + (pw - sw) / 2;
        let sy = Math.floor(faceIndex / 4) * ph + (ph - sh) / 2;
        ImageManager.loadFace(faceName).then(source => {
            if (source) {
                let resource = source.resource;
                this.contents.blt(resource, sx, sy, sw, sh, dx, dy);
                ImageManager.releaseFace(faceName);
            }
        });
    }

    drawCharacter(characterName, characterIndex, x, y) {
        ImageManager.loadCharacter(characterName).then(source => {
            let big = isBigCharacter(characterName);
            let pw = source.width / (big ? 3 : 12);
            let ph = source.height / (big ? 4 : 8);
            let n = characterIndex;
            let sx = (n % 4 * 3 + 1) * pw;
            let sy = (Math.floor(n / 4) * 4) * ph;
            this.contents.blt(source.resource, sx, sy, pw, ph, x, y - ph);
            ImageManager.releaseCharacter(characterName);
        });
    }

    drawLine(x,y,dx,dy,color,width){
        this.contents.drawLine(x,y,dx,dy,color,width);
    }

    drawGauge(x, y, width, rate, color1, color2) {
        let fillW = Math.floor(width * rate);
        let gaugeY = y + this.lineHeight() - 8;
        this.contents.fillRect(x, gaugeY, width, 6, ColorExtraction.gaugeBackColor());
        this.contents.gradientFillRect(x, gaugeY, fillW, 6, color1, color2);
    }
    drawActorCharacter(actor, x, y) {
        this.drawCharacter(actor.characterName(), actor.characterIndex(), x, y);
    }

    drawActorFace(actor, x, y, width, height) {
        this.drawFace(actor.faceName(), actor.faceIndex(), x, y, width, height);
    }

    drawActorName(actor, x, y, width) {
        width = width || 168;
        this.changeTextColor(ColorExtraction.hpColor(actor));
        this.drawText(actor.name(), x, y, width);
    }

    drawActorClass(actor, x, y, width) {
        width = width || 168;
        this.resetTextColor();
        this.drawText(actor.currentClass().name, x, y, width);
    }

    drawActorNickname(actor, x, y, width) {
        width = width || 270;
        this.resetTextColor();
        this.drawText(actor.nickname(), x, y, width);
    }

    drawActorLevel(actor, x, y) {
        this.changeTextColor(ColorExtraction.systemColor());
        this.drawText(TextManager.levelA, x, y, 48);
        this.resetTextColor();
        this.drawText(String(actor.level), x + 48, y, 36);
    }

    drawActorIcons(actor, x, y, width) {
        width = width || 144;
        let icons = actor.allIcons().slice(0, Math.floor(width / config.iconSize));
        for (let i = 0; i < icons.length; i++) {
            this.drawIcon(icons[i], x + config.iconSize * i, y + 2);
        }
    }

    drawCurrentAndMax(current, max, x, y, width, color1, color2) {
        let labelWidth = this.textWidth('HP');
        let valueWidth = this.textWidth('0000');
        let slashWidth = this.textWidth('/');
        let x1 = x + width - valueWidth;
        let x2 = x1 - slashWidth;
        let x3 = x2 - valueWidth;
        let lh=this.lineHeight();
        if (x3 >= x + labelWidth) {
            this.changeTextColor(color1);
            this.drawText(current, x3, y, valueWidth,lh,'right');
            this.changeTextColor(color2);
            this.drawText('/', x2, y, slashWidth,lh, 'right');
            this.drawText(max, x1, y, valueWidth,lh, 'right');
        } else {
            this.changeTextColor(color1);
            this.drawText(current, x1, y, valueWidth,lh, 'right');
        }
    }

    drawActorHp(actor, x, y, width) {
        width = width || 186;
        let color1 = ColorExtraction.hpGaugeColor1();
        let color2 = ColorExtraction.hpGaugeColor2();
        this.drawGauge(x, y, width, actor.hpRate(), color1, color2);
        this.changeTextColor(ColorExtraction.systemColor());
        this.drawText(TextManager.hpA, x, y, 44);
        this.drawCurrentAndMax(actor.hp, actor.mhp, x, y, width,
            ColorExtraction.hpColor(actor),
            ColorExtraction.normalColor());
    }

    drawActorMp(actor, x, y, width) {
        width = width || 186;
        let color1 = ColorExtraction.mpGaugeColor1();
        let color2 = ColorExtraction.mpGaugeColor2();
        this.drawGauge(x, y, width, actor.mpRate(), color1, color2);
        this.changeTextColor(ColorExtraction.systemColor());
        this.drawText(TextManager.mpA, x, y, 44);
        this.drawCurrentAndMax(actor.mp, actor.mmp, x, y, width,
            ColorExtraction.mpColor(actor),
            ColorExtraction.normalColor());
    }

    drawActorTp(actor, x, y, width) {
        width = width || 96;
        let color1 = ColorExtraction.tpGaugeColor1();
        let color2 = ColorExtraction.tpGaugeColor2();
        this.drawGauge(x, y, width, actor.tpRate(), color1, color2);
        this.changeTextColor(ColorExtraction.systemColor());
        this.drawText(TextManager.tpA, x, y, 44);
        this.changeTextColor(ColorExtraction.tpColor(actor));
        this.drawText(actor.tp, x + width - 64, y, 64,this.lineHeight(), 'right');
    }

    drawActorSimpleStatus(actor, x, y, width) {
        let lineHeight = this.lineHeight();
        let x2 = x + 180;
        let width2 = Math.min(200, width - 180 - this.textPadding());
        this.drawActorName(actor, x, y);
        this.drawActorLevel(actor, x, y + lineHeight * 1);
        this.drawActorIcons(actor, x, y + lineHeight * 2);
        this.drawActorClass(actor, x2, y);
        this.drawActorHp(actor, x2, y + lineHeight * 1, width2);
        this.drawActorMp(actor, x2, y + lineHeight * 2, width2);
    }

    drawItemName(item, x, y, width) {
        width = width || 312;
        if (item) {
            let iconBoxWidth = config.iconSize + 4;
            this.resetTextColor();
            this.drawIcon(item.iconIndex, x + 2, y + 2);
            this.drawText(item.name, x + iconBoxWidth, y, width - iconBoxWidth);
        }
    }

    drawCurrencyValue(value, unit, x, y, width) {
        let unitWidth = Math.min(80, this.textWidth(unit));
        this.resetTextColor();
        this.drawText(value, x, y, width - unitWidth - 6,this.lineHeight(),'right');
        this.changeTextColor(ColorExtraction.systemColor());
        this.drawText(unit, x + width - unitWidth, y, unitWidth,this.lineHeight());
    }

    setBackgroundType(type) { }
    showBackgroundDimmer() { }
    hideBackgroundDimmer() { }
    updateBackgroundDimmer() { }
    refreshDimmerBitmap() { }

    /**
     * 
     * @param {string} text 
     * @param {number} x 
     * @param {number} y 
     */
    drawTextEx(text, x, y) {
        let symbol = '';
        let len = text.length;
        let start = 0;
        let drawX = x;
        let drawY = y;
        let maxHeight = this.lineHeight();
        for (let i = 0; i < len; ++i) {
            symbol = text[i];
            if (symbol == '\\') {
                let state = TextInterpreter.parse(text, i);
                if (!state.failed) {
                    let end = state.endi == null ? i : state.endi;
                    let willDraw = text.slice(start, end);
                    start = state.nexti + 1;
                    let maxWidth = this.textWidth(willDraw);

                    this.drawText(willDraw, drawX, drawY, maxWidth, maxHeight);
                    drawX += maxWidth;
                    if (drawX > this.contentsWidth()) {
                        drawX = 0;
                        drawY += maxHeight;
                        if (drawY > this.contentsHeight()) return;
                    }
                    switch (state.type) {
                        case 'V':
                            drawX += this.drawNumber(
                                TextInterpreter.DismantleVariables(state),
                                drawX,
                                drawY,
                                maxHeight
                            );
                            break;
                        case 'C':
                            this.changeTextColor(
                                ColorExtraction.textColor(
                                    TextInterpreter.DismantleVariables(state)
                                )
                            );
                            break;
                        case 'I':
                            this.drawIcon(
                                TextInterpreter.DismantleVariables(state),
                                drawX,
                                drawY
                            );
                            drawX += config.iconSize;
                            if(maxHeight<config.iconSize){
                                maxHeight=config.iconSize;
                            }
                            break;
                        case '{':
                            this.makeFontBigger();
                            maxHeight = this.contents._fontSize + 8;
                            break;
                        case '}':
                            this.makeFontSmaller();
                            if (drawX == 0) {
                                maxHeight = this.contents._fontSize + 8;
                            }
                            break;
                    }
                }
                i = state.nexti;
            } else if (symbol == '\n') {
                let willDraw = text.slice(start, i);
                start = i + 1;
                let maxWidth = this.textWidth(willDraw);
                this.drawText(willDraw, drawX, drawY, maxWidth, maxHeight);
                drawX = 0;
                drawY += maxHeight;
            }
        }
        if (start < len) {
            let willDraw = text.slice(start, len);
            let maxWidth = this.textWidth(willDraw);
            this.drawText(willDraw, drawX, drawY, maxWidth, maxHeight);
        }
    }

    drawNumber(number, x, y, lineHeight, align) {
        let text = String($gameVariables.value(number));
        let maxWidth = this.textWidth(text);
        this.drawText(text, x, y, maxWidth, lineHeight, align);
        return maxWidth;
    }
}